package com.ahdms.es.gm.asn1.generic;

import org.bouncycastle.asn1.*;
import org.bouncycastle.util.Pack;

public class PublicMatrix extends ASN1Object {

    private ASN1Integer keybits;

    private ASN1Integer rows;

    private ASN1Integer columns;

    private ASN1Integer rowbits;

    private ASN1Integer columnbits;

    private ASN1Sequence eccPoints;

    public static PublicMatrix getInstance(Object o) {
        if (o instanceof PublicMatrix) {
            return (PublicMatrix) o;
        } else if (o instanceof ASN1Sequence) {
            return new PublicMatrix((ASN1Sequence) o);
        } else if (o instanceof byte[]) {
            return parseFromOriginalBytes((byte[]) o);
        }

        throw new IllegalArgumentException("unknown object in factory: " + o);
    }

    public PublicMatrix(ASN1Integer keybits,
                        ASN1Integer rows,
                        ASN1Integer columns,
                        ASN1Integer rowbits,
                        ASN1Integer columnbits,
                        ASN1Sequence eccPoints) {
        this.keybits = keybits;
        this.rows = rows;
        this.columns = columns;
        this.rowbits = rowbits;
        this.columnbits = columnbits;
        this.eccPoints = eccPoints;
    }

    public PublicMatrix(ASN1Sequence seq) {
        keybits = ASN1Integer.getInstance(seq.getObjectAt(0));
        rows = ASN1Integer.getInstance(seq.getObjectAt(1));
        columns = ASN1Integer.getInstance(seq.getObjectAt(2));
        rowbits = ASN1Integer.getInstance(seq.getObjectAt(3));
        columnbits = ASN1Integer.getInstance(seq.getObjectAt(4));
        eccPoints = ASN1Sequence.getInstance(seq.getObjectAt(5));
    }

    private static PublicMatrix parseFromOriginalBytes(byte[] data) {
        int pkmLength = 20 + 8 * 64 * 64;

        if (null == data || pkmLength != data.length) {
            throw new IllegalArgumentException("pkm bytes data null or length error.");
        }

        byte[] params = new byte[20];
        System.arraycopy(data, 0, params, 0, 20);

        byte[] key = new byte[4];
        System.arraycopy(params, 0, key, 0, 4);
        ASN1Integer keybits = new ASN1Integer(Pack.littleEndianToInt(key, 0));

        key = new byte[4];
        System.arraycopy(params, 4, key, 0, 4);
        ASN1Integer rows = new ASN1Integer(Pack.littleEndianToInt(key, 0));

        key = new byte[4];
        System.arraycopy(params, 8, key, 0, 4);
        ASN1Integer columns = new ASN1Integer(Pack.littleEndianToInt(key, 0));

        key = new byte[4];
        System.arraycopy(params, 12, key, 0, 4);
        ASN1Integer rowbits = new ASN1Integer(Pack.littleEndianToInt(key, 0));

        key = new byte[4];
        System.arraycopy(params, 16, key, 0, 4);
        ASN1Integer columnbits = new ASN1Integer(Pack.littleEndianToInt(key, 0));

        ASN1EncodableVector points = new ASN1EncodableVector();

        byte[] x = new byte[32];
        byte[] y = new byte[32];
        for (int i = 20; i < data.length; ) {
            System.arraycopy(data, i, x, 0, 32);
            i += 32;
            System.arraycopy(data, i, y, 0, 32);
            i += 32;
            ECCPoint point = new ECCPoint(new ASN1Integer(x), new ASN1Integer(y));
            points.add(point);
        }

        ASN1Sequence eccPoints = new DERSequence(points);

        return new PublicMatrix(keybits, rows, columns, rowbits, columnbits, eccPoints);
    }

    public ASN1Integer getRows() {
        return rows;
    }

    public ASN1Integer getColumns() {
        return columns;
    }

    public ASN1Sequence getEccPoints() {
        return eccPoints;
    }

    public ASN1Integer getKeybits() {
        return keybits;
    }

    public ASN1Integer getRowbits() {
        return rowbits;
    }

    public ASN1Integer getColumnbits() {
        return columnbits;
    }

    @Override
    public ASN1Primitive toASN1Primitive() {
        ASN1EncodableVector v = new ASN1EncodableVector();

        v.add(keybits);
        v.add(rows);
        v.add(columns);
        v.add(rowbits);
        v.add(columnbits);
        v.add(eccPoints);
        return new DERSequence(v);
    }

}
